﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.Util;
using Emgu.CV.UI;
using Emgu.CV.VideoSurveillance;
using Emgu.CV.OCR;

namespace OpenCV_STD
{
    public partial class Form1 : Form
    {

        #region Variable
        

        Capture _capture;   

        Image<Bgr, Byte> _backGroundImg; 

        ProcessEngine engine = new ProcessEngine();

        Form2 son;

        ImageProcess obj;

        #endregion

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            string ans = string.Format("Planar, X:{0:0.000},Y:{1:0.000};   ",
                new object[] { 0,0 });


            obj = new ImageProcess("in.txt");

            _capture = new Capture(0);

            Image<Bgr, Byte> hist = new Image<Bgr, byte>(300, 300);

            foreach (var item in obj.shotList)
            {
                if (item.OK)
                {
                    hist.Draw(new CircleF(new PointF((float)item.cameraPlanarPos[0, 0] * 300, (float)item.cameraPlanarPos[1, 0] * 300), 2), new Bgr(Color.Blue), 1);
                }
            }

            imageBox2.Image = hist;

            //            GetBackground();

            Application.Idle += ProcessFrame;

            //imageBox1.FunctionalMode = Emgu.CV.UI.ImageBox.FunctionalModeOption.RightClickMenu;

//            MessageBox.Show(Emgu.CV.GPU.GpuInvoke.HasCuda.ToString());

            //son = new Form2();

            //son.Show();
        }

        #region JUNKS !!!!!!!!

        private Rectangle enlarge(Rectangle rect)
        {
            return new Rectangle(
                rect.Location.X * 2,
                rect.Location.Y * 2,
                rect.Width * 2,
                rect.Height * 2
                );
        }

        private Rectangle shrink(Rectangle rect)
        {
            return new Rectangle(
                rect.Location.X / 2,
                rect.Location.Y / 2,
                rect.Width / 2,
                rect.Height / 2
                );
        }

        #endregion


        private void GetBackground()
        {
            _backGroundImg = _capture.QueryFrame();
            _backGroundImg.SetZero();

            int num = 5;

            for (int i = 0; i < 20; i++)
            {
                var temp = _capture.QueryFrame();
                _backGroundImg = _backGroundImg.AddWeighted(temp, 1, (double)1 / num, 0);
            }

        }


        //        AdaptiveSkinDetector detector = new AdaptiveSkinDetector(1, AdaptiveSkinDetector.MorphingMethod.ERODE_DILATE);
        FGDetector<Bgr> FGdetector = new FGDetector<Bgr>(Emgu.CV.CvEnum.FORGROUND_DETECTOR_TYPE.FGD);
        BlobTracker tracker = new BlobTracker(Emgu.CV.CvEnum.BLOBTRACKER_TYPE.MSFG);
        

        /// <summary>
        /// Main Loop Here
        /// </summary>
        /// <param name="sedner"></param>
        /// <param name="arg"></param>
        /// FPS is calculated
        Stopwatch stopwatchFrame = new Stopwatch();
        private void ProcessFrame(object sedner, EventArgs arg)
        {
            if (!faceControlToolStripMenuItem.Checked) return;

        // Timer
        // Compute FPS
            stopwatchFrame.Reset();
            stopwatchFrame.Start();


            // PREVIEW
            var imgSmall = _capture.QuerySmallFrame();

            var imgHSV = new Image<Bgr, Byte>(imgSmall.Size);
            CvInvoke.cvCvtColor(imgSmall, imgHSV, Emgu.CV.CvEnum.COLOR_CONVERSION.CV_BGR2HSV);

            var HSVs = imgHSV.Split();
            imageBox3.Image = HSVs[0];
            //imageBox4.Image = HSVs[1];
            imageBox5.Image = HSVs[2];



            // SKIN DETECTOR
            var frameRaw = _capture.QueryFrame();
            var imgYCC = new Image<Ycc, Byte>(imgSmall.Size);
            CvInvoke.cvCvtColor(imgSmall, imgYCC, Emgu.CV.CvEnum.COLOR_CONVERSION.CV_BGR2YCrCb);
            var imgMask = new Image<Gray, Byte>(frameRaw.Size);
            Image<Gray, Byte> mask;
            Image<Gray, Byte> maskSmall;

            // YCbCr skin color
            Image<Gray, Byte>[] YCCs = imgYCC.Split();
            maskSmall = YCCs[1].Cmp(128, Emgu.CV.CvEnum.CMP_TYPE.CV_CMP_GT);
            maskSmall._And(YCCs[1].Cmp(0.67 * 255, Emgu.CV.CvEnum.CMP_TYPE.CV_CMP_LT));
            maskSmall._And(YCCs[2].Cmp(0.32 * 255, Emgu.CV.CvEnum.CMP_TYPE.CV_CMP_GT));
            maskSmall._And(YCCs[2].Cmp(0.52 * 255, Emgu.CV.CvEnum.CMP_TYPE.CV_CMP_LT));
            // HSV filter
            maskSmall._And(HSVs[1].ThresholdBinary(new Gray(30), new Gray(255)));

            maskSmall._Erode(3);
            maskSmall._Dilate(3);

            mask = maskSmall.Resize(2, Emgu.CV.CvEnum.INTER.CV_INTER_NN);

            

          // Face Detect
            // Define search-bound
                // get skin color bound
            var skinBoundingRect = CvInvoke.cvBoundingRect(maskSmall, true);
            skinBoundingRect.Inflate(20, 20);

                // search bound according to 
                    // last frame face( if found )
                    // or skin color( else )
            Rectangle searchBound = new Rectangle();
            if (engine.foundFace)
            {
                searchBound = engine.GetFirstFace();
                searchBound.Inflate(40, 40);
            }
            else
            {
                searchBound = enlarge(skinBoundingRect);
            }
            imgSmall.Draw(searchBound, new Bgr(Color.Red), 2);
            imgSmall.ROI = shrink( searchBound);
            frameRaw.Draw(searchBound, new Bgr(Color.Red), 2);
            frameRaw.ROI = searchBound;


            // Find Face
            Image<Bgr, Byte> imgFaceShow = imgSmall;
            if (skinBoundingRect.Height > 0 && skinBoundingRect.Width > 0)
            {
                imgFaceShow = engine.FindFace(frameRaw);
            }
            imgSmall.ROI = new Rectangle(0, 0, 0, 0);
            imgFaceShow.ROI = new Rectangle();


            Image<Bgr, Byte> lefteye = null, righteye = null;
            //if (engine.GetEyeRects() != null && engine.GetEyeRects().Length > 0)
            //{
            //    // Get Eye pictures
            //    frameRaw.ROI = engine.GetEyeRects()[0];
            //    lefteye = frameRaw.Copy();
            //    frameRaw.ROI = engine.GetEyeRects()[1];
            //    righteye = frameRaw.Copy();
            //}
            // call Kalman Filter
            var faceRect = engine.GetFirstFace();
            PointF center = new PointF(faceRect.Left + faceRect.Width / 2, faceRect.Top + faceRect.Height / 2);
            //PointF center = new PointF(faceRect.Left, faceRect.Top);
            PointF correctedCenter = engine.filterPoints(center)[0];
            imgFaceShow.ROI = new Rectangle();
            frameRaw.Draw(new CircleF(correctedCenter, 3), new Bgr(Color.Green), 2);


            frameRaw.ROI = faceRect;
            imageBox6.Image = frameRaw;
            frameRaw.ROI = new Rectangle();


          // Show Face Details
            listBox1.Items.Clear();
            if (engine.GetFaceRect()!=null)
            {
                foreach (var item in engine.GetFaceRect())
                {
                    listBox1.Items.Add(item.ToString());
                }
            }

            ////////=====  refresh imagebox  =======////////
//            imageBox1.Image = frameRaw.And(mask.Convert<Bgr, Byte>());
//            imageBox2.Image = frameRaw.And(mask.Convert<Bgr, Byte>()).Resize(0.5, Emgu.CV.CvEnum.INTER.CV_INTER_NN);
            if (imgFaceShow != null)
            {
                imgFaceShow.ROI = new Rectangle();
//                imageBox2.Image = imgFaceShow;
            }
            if (engine.GetFaceRect() != null && engine.GetFaceRect().Length > 0)
            {
                frameRaw.Draw(engine.GetFaceRect()[0], new Bgr(Color.Blue), 1);
            }
            if (engine.GetEyeRects() != null && engine.GetEyeRects().Length > 0)
            {
                frameRaw.Draw(engine.GetEyeRects()[0], new Bgr(Color.Yellow), 1);
                frameRaw.Draw(engine.GetEyeRects()[1], new Bgr(Color.Yellow), 1);
            }
            imageBox6.Image = frameRaw.Resize(0.5, Emgu.CV.CvEnum.INTER.CV_INTER_NN);

            //imageBoxLeftEye.Image = lefteye;
            //imageBoxRightEye.Image = righteye;




            PointF viewPoint = new PointF(1-((float)correctedCenter.X)/frameRaw.Width, 1-((float)correctedCenter.Y) / frameRaw.Height);
            toolStripStatusLabel1.Text = viewPoint.ToString();
            //son.SetEye(viewPoint.X, viewPoint.Y);
            //                son.SetEye(FaceRect[0].X + FaceRect[0].Width / 2, FaceRect[0].Y + FaceRect[0].Height / 2);


            var state = engine.kal.CorrectedState;
            matrixBox1.Matrix = state;


            float x = -0.2f + viewPoint.X * 1.4f;
            float y = -0.2f + (1 - viewPoint.Y) * 1.4f;
            freshImage(x, y);

            // Timer
            stopwatchFrame.Stop();
            toolStripStatusLabel2.Text = ( (double)1000 / stopwatchFrame.ElapsedMilliseconds).ToString();
        }
        int phase = 0;

        private void timer1_Tick(object sender, EventArgs e)
        {
            ProcessFrame(this, new EventArgs());

//            button1_Click(this, new EventArgs());
        }


        #region Draw Box

        Rectangle rec = new Rectangle();
        bool recReady = false;
        private void imageBox1_MouseDown(object sender, MouseEventArgs e)
        {
            rec.X = e.X; rec.Y = e.Y;
            recReady = false;
        }

        private void imageBox1_MouseUp(object sender, MouseEventArgs e)
        {
            rec.Width = e.X - rec.X; rec.Height = e.Y - rec.Y;
            recReady = true;
        }

        #endregion



        private void timer2_Tick(object sender, EventArgs e)
        {


        }

        private void Form1_MouseMove(object sender, MouseEventArgs e)
        {


            float x = ((float)e.X - 100) / 50;
            float y = ((float)e.Y - 100) / 50;

            freshImage(x, y);
        }

        void freshImage(float x, float y)
        {
            Image<Bgr, Byte> hist = new Image<Bgr, byte>(300, 300);

            foreach (var item in obj.shotList)
            {
                if (item.OK)
                {
                    hist.Draw(new CircleF(new PointF((float)item.cameraPlanarPos[0, 0] * 300, (float)item.cameraPlanarPos[1, 0] * 300), 2), new Bgr(Color.Blue), 1);
                }
            }
            hist.Draw(new CircleF(new PointF((float)x*300,y*300), 2), new Bgr(Color.Red), 1);

            imageBox2.Image = hist;


            int ind = obj.QueryImage(x, y);
            imageBox1.Image = obj.shotList[ind].img;
            toolStripStatusLabel1.Text = obj.GetInfo(ind);
            if (!faceControlToolStripMenuItem.Selected)
            {
                imageBox3.Image = obj.RenderImage(ind, 0);

                //imageBox4.Image = obj.RenderImage(ind, 1);
                imageBox5.Image = obj.RenderImage(ind, 2);
            }
//            float x = obj.cameraPlanarPos[]
        }

        private void trackBar1_Scroll(object sender, EventArgs e)
        {
            //freshImage((float) trackBar1.Value / 50, (float) trackBar2.Value / 50);
        }

        private void trackBar2_Scroll(object sender, EventArgs e)
        {
            //freshImage((float) trackBar1.Value / 50, (float) trackBar2.Value / 50);
        }

        private void button3_Click(object sender, EventArgs e)
        {
            //for (int i = 0; i < obj.imageIndList.Count; i++)
            //{
            //    imageBox1.Image = obj.imageList[obj.imageIndList[i]];
            //    Emgu.CV.Matrix<double> m1 = new Matrix<double>(obj.cameraPosList[i].toArray);
            //    matrixBox1.Matrix = m1;
            //    Emgu.CV.Matrix<double> m2 = new Matrix<double>(obj.cameraPlanarPos[i].toArray);
            //    matrixBox2.Matrix = m2;
            //    MessageBox.Show(Math.Atan2(m1[1, 0], m1[0, 0]).ToString());
            //}
            //return;
        }


        private void imageBox2_MouseMove(object sender, MouseEventArgs e)
        {
            float x = (float)e.X / imageBox2.Width;
            float y = (float)e.Y / imageBox2.Height;

            freshImage(x, y);
        }

        private void 视频输入ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Form3 form3 = new Form3();
            form3.ShowDialog();
            form3.Dispose();
        }

        private void imageBox2_MouseClick(object sender, MouseEventArgs e)
        {
            float x = (float)e.X / imageBox2.Width;
            float y = (float)e.Y / imageBox2.Height;

            int ind = obj.QueryImage(x, y);
            obj.shotList[ind].OK = false;
        }

        private void faceControlToolStripMenuItem_Click(object sender, EventArgs e)
        {
            faceControlToolStripMenuItem.Checked = !faceControlToolStripMenuItem.Checked;
        }

    }
}
